﻿#define DEBUG
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;

namespace System
{
    public enum LogLevel : int
    {
        Debug,
        Info,
        Warn,
        Error
    }

    /// <summary>
    /// 日志储存类(日志储存路径为程序根目录。)
    /// </summary>
    public class Log
    {
        #region 静态构造

        static object UnhandledExceptionLock = new object();

        static Log()
        {
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
            SaveLevel = LogLevel.Info;
            logThread = new Thread(LogThread);
            logThread.Start();
        }

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            if (!Directory.Exists(LogPath)) Directory.CreateDirectory(LogPath);
            lock (UnhandledExceptionLock)
            {
                FileStream FS = new FileStream(LogPath + "UnhandledException.log", FileMode.OpenOrCreate, FileAccess.Write);
                BinaryWriter BW = new BinaryWriter(FS);
                BW.Seek(0, SeekOrigin.End);
                BW.Write(Encoding.UTF8.GetBytes(e.ExceptionObject.ToString() + "\r\n\r\n"));
                BW.Close();
                FS.Close();
            }
        }

        public static LogLevel SaveLevel { get; set; }

        #endregion

        #region 储存接口

        public static void Debug(string message)
        {
            StringBuilder SB = GetDateTime();
            SB.Append(message);
            SB.Append("\r\n");
            message = SB.ToString();
#if DEBUG

            Console.ForegroundColor = ConsoleColor.Gray;
            Console.Write(message);
#endif
            if (SaveLevel == LogLevel.Debug)
                Save(message);
        }

        public static void Info(string message)
        {
            StringBuilder SB = GetDateTime();
            SB.Append(message);
            SB.Append("\r\n");
            message = SB.ToString();
#if DEBUG
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write(message);
#endif
            if (SaveLevel <= LogLevel.Info)
                Save(message);
        }

        public static void Warn(string message)
        {
            StringBuilder SB = GetDateTime();
            SB.Append(message);
            SB.Append("\r\n");
            message = SB.ToString();
#if DEBUG
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.Write(message);
#endif
            if (SaveLevel <= LogLevel.Warn)
                Save(message);
        }

        public static void Warn(string message, Exception e)
        {
            StringBuilder SB = GetDateTime();
            SB.Append(message + e.Message);
            SB.Append("\r\n");
            message = SB.ToString();
#if DEBUG
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.Write(message + e.Message);
#endif
            if (SaveLevel <= LogLevel.Warn)
                Save(message + e.Message);
        }

        public static void Error(string message) { Save(GetErrorMsg(message, null)); }

        public static void Error(Exception err) { Save(GetErrorMsg("", err)); }

        public static void Error(string message, Exception err)
        {
            Save(GetErrorMsg(message, err));
        }

        private static string GetErrorMsg(string message, Exception err)
        {
            StringBuilder SB = GetDateTime();
            try
            {
                MethodBase mb = new System.Diagnostics.StackTrace().GetFrame(2).GetMethod();

                SB.Append("异常方法:");
                SB.Append(mb.ReflectedType.FullName);
                SB.Append(".");
                SB.Append(mb.Name);
                SB.Append("(");
                ParameterInfo[] PiList = mb.GetParameters();
                for (int i = 0; i < PiList.Length; i++)
                {
                    if (i != 0)
                        SB.Append(",");
                    SB.Append(PiList[i].ToString());
                }
                SB.Append(")\r\n");
                SB.Append("程序集位置:");
                SB.Append(mb.Module.FullyQualifiedName);
                SB.Append("\r\n");
            }
            catch { }
            if (message != "")
            {
                SB.Append(message);
                if (err != null) SB.Append("\r\n");
            }
            if (err != null)
            {
                SB.Append("详细信息:");
                SB.Append(err);
            }
            SB.Append("\r\n");
            string splitStr = "".PadLeft(120, '-') + "\r\n";
            message = splitStr + SB.ToString() + splitStr;
#if DEBUG
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write(message);
#endif
            return message;
        }

        private static void Save(string message)
        {
            try
            {
                lock (TaskList)
                {
                    TaskList.Enqueue(message + "\r\n");
                }
                if (logThread.ThreadState == ThreadState.WaitSleepJoin) logThread.Interrupt();
            }
            catch { }
        }

        private static StringBuilder GetDateTime()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("[");
            sb.Append(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            sb.Append("] ");
            return sb;
        }

        #endregion

        #region 日志线程

        private static Queue<string> TaskList = new Queue<string>();
        private static Thread logThread = null;

        private static void LogThread()
        {
            while (true)
            {
                try
                {
                    string log = null;
                    lock (TaskList)
                    {
                        StringBuilder SB = new StringBuilder();
                        while (TaskList.Count > 0 && SB.Length < 1048576)
                            SB.Append(TaskList.Dequeue());
                        log = SB.ToString();
                    }
                    if (string.IsNullOrEmpty(log))
                    {
                        TaskList.TrimExcess();
                        Thread.Sleep(1000 * 3600);
                    }
                    else
                    {
                        if (!Directory.Exists(LogPath)) Directory.CreateDirectory(LogPath);

                        FileStream FS = new FileStream(LogPath + DateTime.Now.ToString("yyyyMMdd") + ".log", FileMode.OpenOrCreate, FileAccess.Write,FileShare.Write);
                        BinaryWriter BW = new BinaryWriter(FS);
                        BW.Seek(0, SeekOrigin.End);
                        BW.Write(Encoding.UTF8.GetBytes(log));
                        BW.Close();
                        FS.Close();
                    }
                }
                catch { }
            }
        }

        #endregion

        #region 日志保存路径

        private static string logPath = "";

        protected static string LogPath
        {
            get
            {
                if (string.IsNullOrEmpty(logPath))
                {
                    logPath = AppDomain.CurrentDomain.BaseDirectory;
                    logPath += logPath[logPath.Length - 1] != '\\' ? "\\Log\\" : "Log\\";
                }
                return logPath;
            }
        }

        #endregion
    }
}
